﻿#include "Ecc.h"
#include "XyNet/Crypto/X917Random.h"

using namespace CryptoPP;

namespace XyNet
{
	Ecc::KeyType Ecc::_defaultKeyType = Ecc256;

	Ecc::Decryptor::Decryptor(KeyType keyType)
	{
		setRandomKey(keyType);
	}

	Ecc::Decryptor::Decryptor(const Bytes& key)
	{
		setPrivateKey(key);
	}

	Bytes Ecc::Decryptor::getPrivateKey() const
	{
		Bytes output;
		StringSink sink(output);
		_decryptor.GetPrivateKey().Save(sink);
		return output;
	}

	void Ecc::Decryptor::setPrivateKey(const Bytes& key)
	{
		StringSource source(key, true);
		_decryptor.AccessPrivateKey().Load(source);
	}

	void Ecc::Decryptor::setRandomKey(KeyType keyType)
	{
		if (keyType == EccDefault)
			keyType = _defaultKeyType;

		AlgorithmParameters parameters;
		switch (keyType)
		{
		case Ecc160:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP160r1());
			break;
		case Ecc192:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP192r1());
			break;
		case Ecc224:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP224r1());
			break;
		case Ecc256:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP256r1());
			break;
		case Ecc320:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP320r1());
			break;
		case Ecc384:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP384r1());
			break;
		case Ecc512:
			parameters = MakeParameters(Name::GroupOID(), ASN1::brainpoolP512r1());
			break;
		default:
			assert(false);
		}

		static mutex m;
		unique_lock<mutex> l(m);
		_decryptor.AccessKey().GenerateRandom(*X917Random::getDefault(), parameters);
	}

	Bytes Ecc::Decryptor::decrypt(const Bytes& input)
	{
		Bytes output;
		auto decryptor = _decryptor;
		StringSource(input, true,
			new PK_DecryptorFilter(*X917Random::getDefault(), decryptor, new StringSink(output)));
		return output;
	}

	//////////////////////////////////////////////////////////////////////////

	Ecc::Encryptor::Encryptor()
	{

	}

	Ecc::Encryptor::Encryptor(const Decryptor& decryptor)
	{
		setKey(decryptor);
	}

	Ecc::Encryptor::Encryptor(const Bytes& key)
	{
		setPublicKey(key);
	}

	void Ecc::Encryptor::setKey(const Decryptor& decryptor)
	{
		_encryptor.AccessKey().AssignFrom(decryptor._decryptor.GetMaterial());
	}

	Bytes Ecc::Encryptor::getPublicKey() const
	{
		Bytes output;
		StringSink sink(output);
		_encryptor.GetPublicKey().Save(sink);
		return output;
	}

	void Ecc::Encryptor::setPublicKey(const Bytes& key)
	{
		StringSource source(key, true);
		_encryptor.AccessPublicKey().Load(source);
	}

	Bytes Ecc::Encryptor::encrypt(const Bytes& input)
	{
		Bytes output;
		auto encryptor = _encryptor;
		StringSource(input, true,
			new PK_EncryptorFilter(*X917Random::getDefault(), encryptor, new StringSink(output)));
		return output;
	}

	Ecc::KeyType Ecc::getDefaultKeyType()
	{
		return _defaultKeyType;
	}

	void Ecc::setDefaultKeyType(KeyType keyType)
	{
		assert(keyType != EccDefault);
		_defaultKeyType = keyType;
	}

}